\subsection{Cache partilhado}
\label{sec:shared-cache}

Como foi visto na secção anterior o cursor com cache individual será vantajoso numa situação em os threads trabalham sobre gamas de linhas díspares e distantes, no entanto provoca a replicação de informação transmitida e guardada no cliente. 

Esta secção apresenta a implementação do cursor cliente, em que as várias instâncias do cursor partilham o mesmo cache.

O cache partilhado tem as seguintes vantagens:
\begin{enumerate}
  \item Só existe uma cópia dos dados do result set.
  \item A actualização do cache realizada por um cursor cliente é visível pelos restantes cursores cliente.
\end{enumerate}

O cache partilhado tem as seguintes desvantagens:
\begin{enumerate}
  \item Todo o acesso ao cache, seja para leitura ou escrita, tem que ser precedido de um \gls{lock} no objecto que representa o cache.
  \item O número de mensagens trocadas com o servidor pode aumentar, diminuindo o desempenho da aplicação, principalmente numa situação em que os cursores cliente trabalhem em gamas de linhas distantes entre si.
\end{enumerate}

Se o cache for partilhado nas operações de leitura (getInt, getDate, ...) é necessário recuperar o contexto, isto é, verificar se a linha para a qual se moveu o cursor cliente ainda está presente no cache, e efectuar um \gls{fetch} caso não esteja em cache. Num ambiente multithreaded é muito frequente outro thread entrar em execução e alterar as linhas que estão presentes no cache. Isto provoca uma maior troca de mensagens entre o cliente e o servidor porque é necessário alterar várias vezes o conteúdo do cache. 
Tentando resolver este problema, na implementação do cursor com cache partilhado são seleccionadas e guardadas em cache todas as linhas do \gls{dataset}. Isto transforma o \gls{hitratio} em 100\%.

A Figura \ref{img:cjbcs-overview} apresenta o diagrama de classes da solução com cache partilhado. Podemos ver que a relação entre os cursores cliente e o cache é de muitos para 1; a mesma memória cache (\emph{ITDSResultSet}) é utilizada por vários cursores cliente.

\begin{figure}[H]
  \caption{Diagrama de classes da implementação do cursor com cache partilhado.}
  \label{img:cjbcs-overview}
  \centering
  \includegraphics[width=8cm]{images/model/cjdbcs-overview}
\end{figure}

Os atributos e os métodos de \emph{CursorSharedCache} não são apresentados, porque essencialmente o que a classe faz é reimplementar (\gls{override}) os métodos da interface \emph{ResultSet}, adequando-os ao facto de agora estar a operar com um cache partilhado. 
A Tabela \ref{tab:sharedcacheoverride} apresenta os métodos que têm de ser reimplementados pela classe CursorSharedCache.

\begin{center}
{\small
  \begin{longtable}{|l|p{12cm}|}
  \caption{Métodos reimplementados pela classe CursorSharedCache}
  \label{tab:sharedcacheoverride}
  \\ \hline
  \head{Método} & \head{Explicação}
  \\ \hline
  \endhead
  \texttt{afterLast} & A implementação normal deste método invoca o \glssymbol{rpc} sp\_cursorfetch com o optype FETCH AFTER (\ref{sec:fetchscroll}), o que faz com que o cursor do servidor seja movido para uma posição a seguir à última linha e o buffer fique vazio. Com cache partilhado não há a necessidade de mover o cursor do servidor, apenas o cursor cliente. Também não é desejável que o cache seja esvaziado.
  \\ \hline
  \texttt{beforeFirst} & A situação é semelhante ao afterLast, mas o optype é FETCH BEFORE (\ref{sec:fetchscroll}), e o cursor do servidor é movido para uma posição anterior à primeira linha do result set.
  \\ \hline
  \texttt{getInt} & O acesso ao cache para leitura só pode ocorrer depois da obtenção do lock.
  \\ \hline
  \texttt{getString} & O acesso ao cache para leitura só pode ocorrer depois da obtenção do lock.
  \\ \hline
  \texttt{getDouble} & O acesso ao cache para leitura só pode ocorrer depois da obtenção do lock.
  \\ \hline
  \texttt{getDate} & O acesso ao cache para leitura só pode ocorrer depois da obtenção do lock.
  \\ \hline
  \texttt{updateRow} & O acesso ao cache para actualizar uma linha só pode ocorrer depois da obtenção do lock.
  \\ \hline
  \texttt{insertRow} & O acesso ao cache para inserir uma linha só pode ocorrer depois da obtenção do lock.
  \\ \hline
  \texttt{scroll} & O método scroll serve de \gls{boilerplate} para os métodos de navegação no result set. Como no cache partilhado todas as linhas do result set já se encontram no cache, não há a necessidade de verificar se uma linha se encontra em cache e em caso negativo pedi-la ao servidor.
  \\ \hline
  \end{longtable}
}
\end{center}

A partilha do cache implica a utilização de um mecanismo de sincronização. O cursor cliente com cache partilhado não só tem de garantir acesso exclusivo ao canal de comunicação com o servidor, como tem também de garantir o acesso exclusivo ao cache. 
Este aspecto parece constituir uma desvantagem em relação à implementação com cache individual, sendo um potencial factor de perda desempenho.
No entanto, com o cache partilhado não existe o \gls{overhead} da construção de vários caches.
Esta discussão ficará para a secção de análise dos resultados (secção \ref{sec:analiseresultados}) apresentados no capítulo \ref{chap:resultados}.

As classes CursorForwardOnlyReadOnly, CursorForwardOnlyUpdatable, CursorScroll\-ableRead\-Only e CursorScrollableUpdatable implementam um result set do tipo FORWARD\-\_ONLY/READ\_ONLY, ~~FOR\-WARD\_ONLY/UPDATABLE, ~~SCROLLABLE\_SENSITIVE\\/READ\_ONLY e SCROLLABLE\_UPDATABLE, respectivamente. Estas classes reimplementam os métodos que o respectivo result set não suporta, indicando um erro se esses métodos forem invocados.
